interface HttpClientOptions {
  baseURL: string;
  timeout: number;
  headers: Record<string, string>;
  retries: number;
  requestInterceptors: Array<(config: RequestOptions) => RequestOptions>; // 请求拦截器
  responseInterceptors: Array<(response: any) => any>; // 响应拦截器
}

interface RequestOptions extends RequestInit {
  body?: string | null;
}

class HttpClient {
  // 默认配置
  static defaultOptions: HttpClientOptions = {
    baseURL: "",
    timeout: 10000, // 请求超时 10 秒
    headers: {
      "Content-Type": "application/json",
    },
    retries: 3,
    requestInterceptors: [],
    responseInterceptors: [],
  };

  // 动态获取 wx 对象
  static wx: any = typeof window !== "undefined" ? window["wx"] : undefined;

  // 动态获取 tt 对象
  static tt: any = typeof window !== "undefined" ? window["tt"] : undefined;
  /**
   * 判断是否是抖音平台
   */
  static isTt() {
    return typeof this.tt !== "undefined";
  }

  /**
   * 判断是否是微信小程序环境
   */
  static isWeChatMiniProgram() {
    return typeof this.wx !== "undefined" && this.wx.request;
  }

  /**
   * 判断是否是浏览器环境
   */
  static isBrowser() {
    return typeof window !== "undefined" && typeof window.fetch !== "undefined";
  }

  // 添加请求拦截器
  static addRequestInterceptor(
    interceptor: (config: RequestOptions) => RequestOptions
  ): void {
    this.defaultOptions.requestInterceptors.push(interceptor);
  }

  // 添加响应拦截器
  static addResponseInterceptor(interceptor: (response: any) => any): void {
    this.defaultOptions.responseInterceptors.push(interceptor);
  }

  /**
   * 发送 GET 请求
   * @param {string} url 请求的 URL
   * @param {Record<string, string>} params 查询参数
   * @param {Record<string, string>} headers 请求头
   * @returns {Promise<any>} 返回 Promise 对象
   */
  static get(
    url: string,
    params: Record<string, string> = {},
    headers: Record<string, string> = {}
  ): Promise<any> {
    // 拼接查询参数
    const queryString = new URLSearchParams(params).toString();
    const fullUrl = queryString ? `${url}?${queryString}` : url;

    return this.request(fullUrl, "GET", null, headers);
  }

  /**
   * 发送 POST 请求
   * @param {string} url 请求的 URL
   * @param {any} data 请求体数据
   * @param {Record<string, string>} headers 请求头
   * @returns {Promise<any>} 返回 Promise 对象
   */
  static post(
    url: string,
    data: any,
    headers: Record<string, string> = {}
  ): Promise<any> {
    return this.request(url, "POST", data, headers);
  }

  /**
   * 发送 PUT 请求
   * @param {string} url 请求的 URL
   * @param {any} data 请求体数据
   * @param {Record<string, string>} headers 请求头
   * @returns {Promise<any>} 返回 Promise 对象
   */
  static put(
    url: string,
    data: any,
    headers: Record<string, string> = {}
  ): Promise<any> {
    return this.request(url, "PUT", data, headers);
  }

  /**
   * 发送 DELETE 请求
   * @param {string} url 请求的 URL
   * @param {Record<string, string>} headers 请求头
   * @returns {Promise<any>} 返回 Promise 对象
   */
  static delete(
    url: string,
    headers: Record<string, string> = {}
  ): Promise<any> {
    return this.request(url, "DELETE", null, headers);
  }

  /**
   * 发送请求
   * @param {string} url 请求的 URL
   * @param {string} method 请求方法
   * @param {any | null} data 请求体数据
   * @param {Record<string, string>} headers 请求头
   * @returns {Promise<any>} 返回 Promise 对象
   */
  private static request(
    url: string,
    method: string,
    data: any | null = null,
    headers: Record<string, string> = {}
  ): Promise<any> {
    // 合并配置
    const options: RequestOptions = {
      method: method,
      headers: { ...this.defaultOptions.headers, ...headers },
      body: data ? JSON.stringify(data) : null, // POST 和 PUT 请求时将数据转换为 JSON 字符串
    };

    // 请求的完整 URL（如果有 baseURL）
    const fullUrl = this.defaultOptions.baseURL + url;

    // 执行请求拦截器
    const interceptedConfig = this._applyRequestInterceptors(options);

    // 返回一个 Promise，执行请求
    return new Promise((resolve, reject) => {
      if (this.isTt()) {
        // 抖音小程序环境
        window["tt"].request({
          url: fullUrl,
          method: interceptedConfig.method as any,
          data: interceptedConfig.body
            ? JSON.parse(interceptedConfig.body)
            : {},
          header: interceptedConfig.headers,
          timeout: this.defaultOptions.timeout,
          success: (response) => {
            // 执行响应拦截器
            const interceptedResponse = this._applyResponseInterceptors(
              response.data
            );
            resolve(interceptedResponse);
          },
          fail: (error) => {
            if (this._shouldRetry(error)) {
              this._makeRequestWithRetry(
                fullUrl,
                interceptedConfig,
                resolve,
                reject,
                0
              );
            } else {
              reject(error);
            }
          },
        });
      } else if (this.isWeChatMiniProgram()) {
        // 微信小程序环境
        this.wx.request({
          url: fullUrl,
          method: interceptedConfig.method as any,
          data: interceptedConfig.body
            ? JSON.parse(interceptedConfig.body)
            : {},
          header: interceptedConfig.headers,
          timeout: this.defaultOptions.timeout,
          success: (response) => {
            // 执行响应拦截器
            const interceptedResponse = this._applyResponseInterceptors(
              response.data
            );
            resolve(interceptedResponse);
          },
          fail: (error) => {
            if (this._shouldRetry(error)) {
              this._makeRequestWithRetry(
                fullUrl,
                interceptedConfig,
                resolve,
                reject,
                0
              );
            } else {
              reject(error);
            }
          },
        });
      } else if (this.isBrowser()) {
        // 浏览器环境
        fetch(fullUrl, interceptedConfig)
          .then((response) => response.json())
          .then((data) => {
            // 执行响应拦截器
            const interceptedResponse = this._applyResponseInterceptors(data);
            resolve(interceptedResponse);
          })
          .catch((error) => {
            if (this._shouldRetry(error)) {
              this._makeRequestWithRetry(
                fullUrl,
                interceptedConfig,
                resolve,
                reject,
                0
              );
            } else {
              reject(error);
            }
          });
      } else {
        reject(new Error("Unsupported environment"));
      }
    });
  }

  /**
   * 执行请求拦截器
   * @param {RequestOptions} config 请求配置
   * @returns {RequestOptions} 拦截后的请求配置
   */
  private static _applyRequestInterceptors(
    config: RequestOptions
  ): RequestOptions {
    let interceptedConfig = { ...config };
    for (const interceptor of this.defaultOptions.requestInterceptors) {
      interceptedConfig = interceptor(interceptedConfig); // 依次执行请求拦截器
    }
    return interceptedConfig;
  }

  /**
   * 执行响应拦截器
   * @param {any} response 响应数据
   * @returns {any} 拦截后的响应数据
   */
  private static _applyResponseInterceptors(response: any): any {
    let interceptedResponse = response;
    for (const interceptor of this.defaultOptions.responseInterceptors) {
      interceptedResponse = interceptor(interceptedResponse); // 依次执行响应拦截器
    }
    return interceptedResponse;
  }

  /**
   * 发起请求并处理重试
   * @param {string} url 请求的 URL
   * @param {RequestOptions} options 请求选项
   * @param {Function} resolve 成功回调
   * @param {Function} reject 失败回调
   * @param {number} attempt 当前尝试次数
   */
  private static _makeRequestWithRetry(
    url: string,
    options: RequestOptions,
    resolve: (value: any) => void,
    reject: (reason?: any) => void,
    attempt: number
  ): void {
    if (attempt < this.defaultOptions.retries) {
      console.log(`Retrying request... Attempt ${attempt + 1}`);
      this.request(
        url,
        options.method as string,
        options.body ? JSON.parse(options.body) : null,
        options.headers as Record<string, string>
      )
        .then(resolve)
        .catch(reject);
    } else {
      reject(new Error("Max retries exceeded"));
    }
  }

  /**
   * 判断是否需要重试
   * @param {any} error 错误对象
   * @returns {boolean} 是否需要重试
   */
  private static _shouldRetry(error: any): boolean {
    // 根据错误类型判断是否需要重试
    return error.errCode === 14 || error.errCode === 15; // 例如网络错误
  }
}

export default HttpClient;
